from django.shortcuts import render
from django.views import View
from django import http
from django_redis import get_redis_connection

import json, pickle, base64
from goods.models import SKU
from meiduo_mall.utils.response_code import RETCODE


class CartsView(View):
    """添加购物车视图"""

    def post(self, request):
        """添加购物车数据"""
        # 获取请求体中的数据
        json_dict = json.loads(request.body.decode())
        sku_id = json_dict.get('sku_id')
        count = json_dict.get('count')
        seleted = json_dict.get('selected', True)
        # 校验
        if not all([sku_id, count]):
            return http.HttpResponseForbidden('参数不全')
        try:
            sku = SKU.objects.get(id=sku_id)
        except SKU.DoesNotExist:
            return http.HttpResponseForbidden('sku_id不存在')
        if isinstance(count, int) is False:
            return http.HttpResponseForbidden('参数类型有误')
        if isinstance(seleted, bool) is False:
            return http.HttpResponseForbidden('参数类型有误')
        # 判断用户是否登录
        user = request.user
        if user.is_authenticated:
            # 登录用户操作redis{carts_user.id:{sku.id:count,...}{selected:[sku.id]}
            # 创建redis连接对象
            redis_conn = get_redis_connection('carts')
            key = 'carts_%s' % user.id
            pl = redis_conn.pipeline()
            # .hincrby会自动对hash里的value增加
            pl.hincrby(key, sku.id, count)
            if seleted:
                pl.sadd('selected_%s' % user.id, sku_id)
            pl.execute()
            return http.JsonResponse({'code': RETCODE.OK, 'errmsg': '购物车添加成功'})

        else:
            # 未登录用户存cookie{sku_id:{count:2,selected:True}}
            # 先从从cookie中取出购物车数据判断是否已存在
            carts_str = request.COOKIES.get('carts')
            if carts_str:
                carts_dict = pickle.loads(base64.b64decode(carts_str.encode()))
                # 判断该商品是否添加过
                if sku_id in carts_dict:
                    # 添加过修改count值

                    count += carts_dict[sku_id]['count']
                    print(type(count))
            else:
                # 准备一个大字典
                carts_dict = {}
            carts_dict[sku_id] = {
                'count': count,
                'selected': seleted
            }
            carts_str = base64.b64encode(pickle.dumps(carts_dict)).decode()
            # 把购物车信息重新保存到cookie
            response = http.JsonResponse({'code': RETCODE.OK, 'errmsg': '添加购物车成功'})
            response.set_cookie('carts', carts_str)
            return response

    def get(self, request):
        """展示购物车数据"""
        # 判断用户是否登录
        user = request.user
        if user.is_authenticated:
            # 登录用户从redis中读取数据{carts_user.id:{sku.id:2}},selected_user.id:[]
            redis_conn = get_redis_connection('carts')
            # 获取hash中的数据 {b'sku.id':b'count',...}
            redis_carts = redis_conn.hgetall('carts_%s' % user.id)
            selected_set = redis_conn.smembers('selected_%s' % user.id)
            # 为了与未登录用户共用一个代码准备一个空字典装所有购物车数据返回
            carts_dict = {}
            for sku_id_byte in redis_carts:
                carts_dict[int(sku_id_byte)] = {
                    'selected': (sku_id_byte in selected_set),
                    'count': int(redis_carts[sku_id_byte])
                }
        else:
            # 未登录用户获取cookie购物车数据
            carts_str = request.COOKIES.get('carts')
            if carts_str:
                # 有cookie购物车数据就将它从字符串转字典
                carts_dict = pickle.loads(base64.b64decode(carts_str.encode()))
            else:
                # 没有cookie购物车数据,就显示一个空白的购物车界面
                return render(request, 'cart.html')
        # 为了sku_id查询sku模型及包装模板需要渲染的数据代码 登录和未登录共用同一个代码
        # 查询sku模型
        sku_qs = SKU.objects.filter(id__in=carts_dict.keys())
        cart_skus = []  # 此列表用来包装前端界面需要渲染的所有购物车商品数据
        for sku in sku_qs:
            # 获取指定商品要购买的数量
            count = carts_dict[sku.id]['count']
            cart_skus.append({
                'id': sku.id,
                'name': sku.name,
                'default_image_url': sku.default_image.url,
                'price': str(sku.price),  # 为了方便js进行解析数据尽量把它转换成str类型
                'count': count,
                'selected': str(carts_dict[sku.id]['selected']),  # 因为js对数据进行转换处理,js中true
                'amount': str(sku.price * count)
            })

        context = {
            'cart_skus': cart_skus
        }
        return render(request, 'cart.html', context)

    def put(self, request):
        """更新购物车"""
        # 接收前非表单数据
        json_dict = json.loads(request.body.decode())
        sku_id = json_dict.get('sku_id')
        count = json_dict.get('count')
        selected = json_dict.get('selected')
        if all([sku_id, count]) is False:
            return http.HttpResponseForbidden('参数不全')
        try:
            sku = SKU.objects.get(id=sku_id)
        except SKU.DoesNotExist:
            return http.HttpResponseForbidden('sku_id不存在')
        if not isinstance(count, int):
            return http.HttpResponseForbidden('参数类型错误')
        if not isinstance(selected, bool):
            return http.HttpResponseForbidden('参数类型错误')
        # 判断用户是否登录
        user = request.user
        if user.is_authenticated:
            # 登录用户操作redis数据库
            redis_conn = get_redis_connection('carts')
            pl = redis_conn.pipeline()
            pl.hset('carts_%s' % user.id, sku.id, count)
            if selected:
                # 沟选就存入redis集合中，如果原来已勾选再存集合会自动去重
                pl.sadd('selected_%s' % user.id, sku.id)
            else:
                # 未勾选就删除redis集合中的sku.id
                pl.srem('selected_%s' % user.id, sku.id)
            pl.execute()
            # 准备一个字典返回
            cart_sku = {
                'id': sku.id,
                'name': sku.name,
                'price': str(sku.price),  # 为了方便js解析转成str
                'default_image_url': sku.default_image.url,
                'count': count,
                'selected': selected,  # js接收'True'
                'amount': str(sku.price * count)
            }
            # 响应
            return http.JsonResponse({'code': RETCODE.OK, 'errmsg': 'OK', 'cart_sku': cart_sku})
        else:
            # 未登录用户操作cookie
            carts_str = request.COOKIES.get('carts')
            if carts_str:
                # 存在取出购物车数据
                carts_dict = pickle.loads(base64.b64decode(carts_str.encode()))
            else:
                # 非正常请求提前响应
                return render(request, 'cart.html')

            # 修改购物车数据
            carts_dict = {
                'count': count,
                'selected': selected
            }
            # 再转化成字符串
            carts_str = base64.b64encode(pickle.dumps(carts_dict)).decode()
            # 准备一个字典响应
            cart_sku = {
                'id': sku.id,
                'name': sku.name,
                'price': str(sku.price),  # 为了方便js解析转成str
                'default_image_url': sku.default_image.url,
                'count': count,
                'selected': selected,  # js接收'True'
                'amount': str(sku.price * count)
            }

            # 响应
            response = http.JsonResponse({'code': RETCODE.OK, 'errmsg': 'OK', 'cart_sku': cart_sku})
            response.set_cookie('carts', carts_str)
            return response

    def delete(self, request):
        """删除购物车"""
        # 接收前非表单数据
        json_dict = json.loads(request.body.decode())
        sku_id = json_dict.get('sku_id')
        # 校验
        try:
            sku = SKU.objects.get(id=sku_id)
        except SKU.DoesNotExist:
            return http.HttpResponseForbidden('sku_id不存在')
        # 判断用户是否登录
        user = request.user
        if user.is_authenticated:
            # 登录用户操作redis数据库
            redis_conn = get_redis_connection('carts')
            pl = redis_conn.pipeline()
            pl.hdel('carts_%s' % user.id, sku_id)
            pl.srem('selected_%s' % user.id, sku_id)
            pl.execute()
            # 响应
            return http.JsonResponse({'code': RETCODE.OK, 'errsmg': '删除成功'})
        else:
            # 未登录用户操作cookie
            cart_str = request.COOKIES.get('carts')
            if cart_str:
                cart_dict = pickle.loads(base64.b64decode(cart_str.encode()))

            else:
                return render(request, 'cart.html')

            if sku_id in cart_dict:
                del cart_dict[sku_id]
                # 响应
            # 创建响应对象
            response = http.JsonResponse({'code': RETCODE.OK, 'errmsg': '删除成功'})
            if not cart_dict:
                response.delete_cookie('carts')
                return response
                # 把cookie字典转回字符串
            cart_str = base64.b64encode(pickle.dumps(cart_dict)).decode()
            # 重新设置cookie
            response.set_cookie('carts', cart_str)
            return response


class CartsSelectedAllView(View):
    """购物车全选"""

    def put(self, request):
        # 接收请求体数据selected
        json_dict = json.loads(request.body.decode())
        selected = json_dict.get('selected')
        # 校验类型
        if isinstance(selected, bool) is False:
            return http.HttpResponseForbidden('参数类型错误')
        # 判断是否登录
        user = request.user
        if user.is_authenticated:
            # 登录用户操作redis购物车数据
            # 创建redis连接对象
            redis_conn = get_redis_connection('carts')
            # 判断selected
            if selected:
                # 取出hash中所有的sku_id
                redis_carts = redis_conn.hgetall('carts_%s' % user.id)
                sku_ids = redis_carts.keys()
                # 把所有sku_id添加到set集合中
                redis_conn.sadd('selected_%s' % user.id, *sku_ids)
            else:
                # 删除set集合
                redis_conn.delete('selected_%s' % user.id)
            # 响应
            return http.JsonResponse({'code': RETCODE.OK, 'errmsg': 'OK'})
        else:
            # 未登录用户操作cookie购物车
            # 获取cookie
            carts_str = request.COOKIES.get('carts')
            # 判断cookie购物车是否有数据
            if carts_str:
                # 有数据把字符串转化为字典
                carts_dict = pickle.loads(base64.b64decode(carts_str.encode()))
            else:
                # 没有数据提前响应
                return http.JsonResponse({'code': RETCODE.DBERR, 'errmsg': 'cookie不存在'})
            # 修改字典中的selected值
            for sku_id in carts_dict:
                carts_dict[sku_id]['selected'] = selected
            # 再把字典转化为字符串
            carts_str = base64.b64encode(pickle.dumps(carts_dict)).decode()
            # 创建响应对象
            response = http.JsonResponse({'code': RETCODE.OK, 'errmsg': 'OK'})
            response.set_cookie('carts', carts_str)
            return response


class SimpleCartsView(View):
    """简单版购物车"""

    def get(self, request):
        """展示购物车数据"""
        # 判断用户是否登录
        user = request.user
        if user.is_authenticated:
            # 登录用户从redis中读取数据{carts_user.id:{sku.id:2}},selected_user.id:[]
            redis_conn = get_redis_connection('carts')
            # 获取hash中的数据 {b'sku.id':b'count',...}
            redis_carts = redis_conn.hgetall('carts_%s' % user.id)
            selected_set = redis_conn.smembers('selected_%s' % user.id)
            # 为了与未登录用户共用一个代码准备一个空字典装所有购物车数据返回
            carts_dict = {}
            for sku_id_byte in redis_carts:
                carts_dict[int(sku_id_byte)] = {
                    'selected': sku_id_byte in selected_set,
                    'count': int(redis_carts[sku_id_byte])
                }
        else:
            # 未登录用户获取cookie购物车数据
            carts_str = request.COOKIES.get('carts')
            if carts_str:
                # 有cookie购物车数据就将它从字符串转字典
                carts_dict = pickle.loads(base64.b64decode(carts_str.encode()))
            else:
                # 没有cookie购物车数据,就显示一个空白的购物车界面
                return render(request, 'cart.html')
        # 为了sku_id查询sku模型及包装模板需要渲染的数据代码 登录和未登录共用同一个代码
        # 查询sku模型
        sku_qs = SKU.objects.filter(id__in=carts_dict.keys())
        cart_skus = []  # 此列表用来包装前端界面需要渲染的所有购物车商品数据
        for sku in sku_qs:
            # 获取指定商品要购买的数量
            count = carts_dict[sku.id]['count']
            cart_skus.append({
                'id': sku.id,
                'name': sku.name,
                'default_image_url': sku.default_image.url,
                'count': count
            })
        return http.JsonResponse({'code': RETCODE.OK, 'errmsg': 'OK', 'cart_skus': cart_skus})
